#include <string>

#include "Functions.h"

using namespace std;

int DoTextOutlinePoint(Point LinePoint, Point SurroundingPoint, SDL_Surface *surface, SDL_Color OutlineColour, SDL_Color TextColour);

int CloseFont(TTF_Font *font)
{
    if (!TTF_WasInit())
    {
         TTF_Init();

         TTF_CloseFont(font);

         TTF_Quit();
    }
    else
    {
        TTF_CloseFont(font);
    }
}

int BlitToSurfacePosition(Point Position, SDL_Surface *surface, SDL_Surface *Image)
{
    SDL_Rect dest;
    dest.x = static_cast<int>(Position.x);
    dest.y = static_cast<int>(Position.y);
    SDL_BlitSurface(Image, NULL, surface, &dest);
}

SDL_Color GetSDLColor(int r, int g, int b)
{
    SDL_Color t;

    t.r = r;
    t.g = g;
    t.b = b;

    return t;
}

bool CompareSDLColours(SDL_Color c1, SDL_Color c2)
{
    if (c1.r == c2.r && c1.g == c2.g && c1.b == c2.b)
    {
        return true;
    }
    else
    {
        return false;
    }
}

PixelData GetPixelData(Point Position, SDL_Surface *surface)
{
    PixelData ret;

    Uint8 *bufp8 = NULL;
    Uint16 *bufp16 = NULL;
    Uint32 *bufp32 = NULL;

    int x = (int)Position.x;
    int y = (int)Position.y;

    switch (surface->format->BytesPerPixel)
    {
        case 1: // 8-bpp
        {
            bufp8 = (Uint8 *)surface->pixels + y*surface->pitch + x;
            SDL_GetRGBA(*bufp8, surface->format, &ret.Colour.r, &ret.Colour.g, &ret.Colour.b, &ret.iAlpha);
        }
        break;

        case 2: // 15-bpp or 16-bpp
        {
            bufp16 = (Uint16 *)surface->pixels + y*surface->pitch/2 + x;
            SDL_GetRGBA(*bufp16, surface->format, &ret.Colour.r, &ret.Colour.g, &ret.Colour.b, &ret.iAlpha);
        }
        break;

        case 3: // Slow 24-bpp mode (not supported)
        {

        }
        break;

        case 4: // 32-bpp
        {
            bufp32 = (Uint32 *)surface->pixels + y*surface->pitch/4 + x;
            SDL_GetRGBA(*bufp32, surface->format, &ret.Colour.r, &ret.Colour.g, &ret.Colour.b, &ret.iAlpha);
        }
        break;
    }

    return ret;
}

void SetPixelData(Point Position, SDL_Surface *surface, PixelData Data)
{
    Uint8 *bufp8 = NULL;
    Uint16 *bufp16 = NULL;
    Uint32 *bufp32 = NULL;

    int x = (int)Position.x;
    int y = (int)Position.y;

    switch (surface->format->BytesPerPixel)
    {
        case 1: // 8-bpp
        {
            bufp8 = (Uint8 *)surface->pixels + y*surface->pitch + x;
            *bufp8 = SDL_MapRGBA(surface->format, (int)Data.Colour.r, (int)Data.Colour.g, (int)Data.Colour.b, Data.iAlpha);
        }
        break;

        case 2: // 15-bpp or 16-bpp
        {
            bufp16 = (Uint16 *)surface->pixels + y*surface->pitch/2 + x;
            *bufp16 = SDL_MapRGBA(surface->format, (int)Data.Colour.r, (int)Data.Colour.g, (int)Data.Colour.b, Data.iAlpha);
        }
        break;

        case 3: // Slow 24-bpp mode (not supported)
        {

        }
        break;

        case 4: // 32-bpp
        {
            bufp32 = (Uint32 *)surface->pixels + y*surface->pitch/4 + x;
            *bufp32 = SDL_MapRGBA(surface->format, (int)Data.Colour.r, (int)Data.Colour.g, (int)Data.Colour.b, Data.iAlpha);
        }
        break;
    }
}

void OutlineText(SDL_Surface *surface, SDL_Color OutlineColour, SDL_Color TextColour)
{
    Uint32 *bufp32 = NULL;

    SDL_Color PixelColour;
    Uint8 iAlpha;

    PixelData TempData;

    for (int y = 0; y < surface->h; y++)
    {
        for (int x = 0; x < surface->w; x++)
        {
            TempData = GetPixelData(Point(x, y), surface);

            if (TempData.iAlpha != SDL_ALPHA_OPAQUE)
            {
                /*  * = Current Pixel

                    1   2   3
                    4   *   5
                    6   7   8
                */

                Point LinePoint = Point(x, y);

                // 1
                DoTextOutlinePoint(LinePoint, Point(x - 1, y - 1), surface, OutlineColour, TextColour);
                // 2
                DoTextOutlinePoint(LinePoint, Point(x, y - 1), surface, OutlineColour, TextColour);
                // 3
                DoTextOutlinePoint(LinePoint, Point(x + 1, y - 1), surface, OutlineColour, TextColour);
                // 4
                DoTextOutlinePoint(LinePoint, Point(x - 1, y), surface, OutlineColour, TextColour);
                // 5
                DoTextOutlinePoint(LinePoint, Point(x + 1, y), surface, OutlineColour, TextColour);
                // 6
                DoTextOutlinePoint(LinePoint, Point(x - 1, y + 1), surface, OutlineColour, TextColour);
                // 7
                DoTextOutlinePoint(LinePoint, Point(x, y + 1), surface, OutlineColour, TextColour);
                // 8
                DoTextOutlinePoint(LinePoint, Point(x + 1, y + 1), surface, OutlineColour, TextColour);
            }
        }
    }
}

int DoTextOutlinePoint(Point LinePoint, Point SurroundingPoint, SDL_Surface *surface, SDL_Color OutlineColour, SDL_Color TextColour)
{
    if (SurroundingPoint.x < 0 || SurroundingPoint.y < 0 || SurroundingPoint.x >= surface->w || SurroundingPoint.y >= surface->h)
    {
        return -1;
    }

    PixelData TempData = GetPixelData(Point(SurroundingPoint.x, SurroundingPoint.y), surface);

    if (TempData.iAlpha == SDL_ALPHA_OPAQUE && CompareSDLColours(TempData.Colour, OutlineColour) == false)
    {
        SetPixelData(LinePoint, surface, PixelData(OutlineColour, SDL_ALPHA_OPAQUE));

        return 0;
    }
}

void SetSurfaceContrast(const int& iValue, SDL_Surface *surface)
{
    SDL_Color OldColour;

    Uint8 iOldAlpha;

    float fContrast = static_cast<float>(pow((100.0f + static_cast<float>(iValue)) / 100.0f, 2));

    for (int y = 0; y < surface->h; y++)
    {
        for (int x = 0; x < surface->w; x++)
        {
            Uint8 *bufp8 = NULL;
            Uint16 *bufp16 = NULL;
            Uint32 *bufp32 = NULL;

            switch (surface->format->BytesPerPixel)
            {
                case 1: // 8-bpp
                {
                    bufp8 = (Uint8 *)surface->pixels + y*surface->pitch + x;
                    SDL_GetRGBA(*bufp8, surface->format, &OldColour.r, &OldColour.g, &OldColour.b, &iOldAlpha);
                }
                break;

                case 2: // 15-bpp or 16-bpp
                {
                    bufp16 = (Uint16 *)surface->pixels + y*surface->pitch/2 + x;
                    SDL_GetRGBA(*bufp16, surface->format, &OldColour.r, &OldColour.g, &OldColour.b, &iOldAlpha);
                }
                break;

                case 3: // Slow 24-bpp mode (not supported)
                {
                    return;
                }
                break;

                case 4: // 32-bpp
                {
                    bufp32 = (Uint32 *)surface->pixels + y*surface->pitch/4 + x;
                    SDL_GetRGBA(*bufp32, surface->format, &OldColour.r, &OldColour.g, &OldColour.b, &iOldAlpha);
                }
                break;
            }

            float fNewR, fNewG, fNewB;

            // Red******************************************************************************************************
            fNewR = static_cast<float>(OldColour.r) / 255.0f;
            fNewR -= 0.5f;
            fNewR *= fContrast;
            fNewR += 0.5f;
            fNewR *= 255.0f;

            if (fNewR > 255.0f)
            {
                fNewR = 255.0f;
            }
            else if (fNewR < 0.0f)
            {
                fNewR = 0.0f;
            }
            //**********************************************************************************************************

            // Green****************************************************************************************************
            fNewG = static_cast<float>(OldColour.g) / 255.0f;
            fNewG -= 0.5f;
            fNewG *= fContrast;
            fNewG += 0.5f;
            fNewG *= 255.0f;

            if (fNewG > 255.0f)
            {
                fNewG = 255.0f;
            }
            else if (fNewG < 0.0f)
            {
                fNewG = 0.0f;
            }
            //**********************************************************************************************************

            // Blue*****************************************************************************************************
            fNewB = static_cast<float>(OldColour.b) / 255.0f;
            fNewB -= 0.5f;
            fNewB *= fContrast;
            fNewB += 0.5f;
            fNewB *= 255.0f;

            if (fNewB > 255.0f)
            {
                fNewB = 255.0f;
            }
            else if (fNewB < 0.0f)
            {
                fNewB = 0.0f;
            }
            //**********************************************************************************************************

            SDL_LockSurface(surface);
            // Set this pixel to the new contrast
            switch (surface->format->BytesPerPixel)
            {
                case 1: // 8-bpp
                {
                    *bufp8 = SDL_MapRGBA(surface->format, (int)fNewR, (int)fNewG, (int)fNewB, iOldAlpha);
                }
                break;

                case 2: // 15-bpp or 16-bpp
                {
                    *bufp16 = SDL_MapRGBA(surface->format, (int)fNewR, (int)fNewG, (int)fNewB, iOldAlpha);
                }
                break;

                case 4: // 32-bpp
                {
                    *bufp32 = SDL_MapRGBA(surface->format, (int)fNewR, (int)fNewG, (int)fNewB, iOldAlpha);
                }
                break;
            }
            SDL_UnlockSurface(surface);
        }
    }
}

Point GetWindowSize()
{
    return Point(ConfigFile::iCurrentWindowResX, ConfigFile::iCurrentWindowResY);
}

SDL_Surface* OptimiseSurface(SDL_Surface *surface)
{
    if (surface == NULL) { return NULL; }

    SDL_Surface *temp = surface;

    surface = SDL_DisplayFormat(temp);

    SDL_FreeSurface(temp);

    return surface;
}

SDL_Surface* OptimiseAlphaSurface(SDL_Surface *surface)
{
    if (surface == NULL) { return NULL; }

    SDL_Surface *temp = surface;

    surface = SDL_DisplayFormatAlpha(temp);

    SDL_FreeSurface(temp);

    return surface;
}
